#include "scene.h"
#include <thread>

Scene::Scene(int width, int height)
:m_openList([](std::shared_ptr<Node> const& elem1, std::shared_ptr<Node> const& elem2){
	if (elem1->f_val == elem2->f_val)
	{
		return elem1->self < elem2->self;
	}
	else
	{
		return elem1->f_val < elem2->f_val;
	}
}), m_height(height)
{
	for (int i = 0; i <= width; ++i)
	{
		std::vector<bool>	closeList;
		std::vector<Point>	points;
		points.reserve(height + 1);
		closeList.reserve(height + 1);
		for (int j = 0; j <= height; ++j)
		{
			points.emplace_back(i, j);
			closeList.push_back(false);
		}
		m_map.emplace_back(std::move(points));
		m_closeList.emplace_back(std::move(closeList));
	}
    ::hide_cursor();
    ::clearcls();
}

void Scene::draw(int x, int y, unsigned int color)
{
    ::draw(x, y, color, "*");
	std::this_thread::sleep_for(std::chrono::milliseconds(100));
}

void Scene::find(Point const& start, Point const& end)
{
	if (start == end)
	{
		return;
	}
	draw(start.x, start.y, GREEN);
	draw(end.x, end.y, GREEN);
	m_closeList[start.x][start.y] = true;		

	AddNewOpenList(std::make_shared<Node>(0, nullptr, &start), end, 0);
	
	auto newStart = m_openList.begin();
	find(*newStart, end);
}

void Scene::showback(std::shared_ptr<Node> const& node)
{
	if (node)
	{
		draw(node->self->x, node->self->y, YELLOW);
		showback(node->parent);
	}
}

void Scene::find(const std::shared_ptr<Node>& start, Point const& end)
{
	if (*start->self == end)
	{
		showback(start);
		return;
	}
	draw(start->self->x, start->self->y, RED);
	m_closeList[start->self->x][start->self->y] = true;	

	auto pos = m_openList.find(start);
	int lastF = 0;

	AddNewOpenList(start, end, lastF);

	if (pos != m_openList.end())
	{
		lastF = (*pos)->f_val;
		
		m_openList.erase(pos);
	}

	auto newStart = m_openList.begin();
	find(*newStart, end);
}

void Scene::AddNewOpenList(std::shared_ptr<Node> const& start, Point const& end, int lastF)
{

	auto func = [&, this](Point const& obj){
		if (CheckPoint(obj))
		{
			auto g = CalcG(obj, *start->self);
			if (g < 0)
			{
				return;
			}
			auto h = CalcH(obj, end);
			auto f = g + h + lastF;
			m_openList.insert(std::make_shared<Node>(f, start, &obj));
		}
	};

	if ((start->self->x - 1) >= 0 && (start->self->y - 1) >= 0)
	{
		Point& point = m_map[start->self->x - 1][start->self->y - 1];
		func(point);
	}

	if ((start->self->x - 1) >= 0)
	{
		Point& point = m_map[start->self->x - 1][start->self->y];
		func(point);
	}

	if ((start->self->x - 1) >= 0 && static_cast<size_t>(start->self->y + 1) < m_map[0].size())
	{
		Point& point = m_map[start->self->x - 1][start->self->y + 1];
		func(point);
	}

	if ((start->self->y - 1) >= 0)
	{
		Point& point = m_map[start->self->x][start->self->y - 1];
		func(point);
	}

	if (static_cast<size_t>(start->self->y + 1) < m_map[0].size())
	{
		Point& point = m_map[start->self->x][start->self->y + 1];
		func(point);
	}

	if (static_cast<size_t>(start->self->x + 1) < m_map.size() && (start->self->y - 1) >= 0)
	{
		Point& point = m_map[start->self->x + 1][start->self->y - 1];
		func(point);
	}

	if (static_cast<size_t>(start->self->x + 1) < m_map.size())
	{
		Point& point = m_map[start->self->x + 1][start->self->y];
		func(point);
	}

	if (static_cast<size_t>(start->self->x + 1) < m_map.size() && static_cast<size_t>(start->self->y + 1) < m_map[0].size())
	{
		Point& point = m_map[start->self->x + 1][start->self->y + 1];
		func(point);
	}
}
